package ca.ubc.eece310.project;

/**
 * EECE 310 Term Project Fall 2009
 * @author Matthew Fong, Herman Lee, Scott Pearson
 */

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;

public class RunningProcess implements Comparable<RunningProcess> {
	/*******************************************************************************
	 * OVERVIEW: A data type designed to keep track of the running processes on
	 * 			 a computer that has ports opened.
	 * Rep-invariant: ports.size>0
	 * AF(c): c.processName, c.ports, c.PID, c.up, c.down
	 *******************************************************************************/
	private String processName;
	private HashSet<MyPort> ports = new HashSet<MyPort>();
	private int PID;
	private double up, down;
	public static ArrayList<RunningProcess> RunningProcesses = new ArrayList<RunningProcess>();
	private CommandExecutor ce = new CommandExecutor();

	final public static int numFields = 4;

	// CONSTRUCTORS
	public RunningProcess(int PID, MyPort port) {
		/**
		 * REQUIRES: port != null 
		 * EFFECTS: Creates a process with PID and port.
		 * Adds itself to RunningProcesses and adds port to this process's ports
		 */
		if (PID != 0) {
			if (!processExists(PID)) {
				this.PID = PID;
				processName = ce.getProcessName(this.PID);
				RunningProcesses.add(this);
				up = 0;
				down = 0;
			}
			RunningProcess thisProcess = findProcessByPID(PID);
			if (!thisProcess.portExists(port.getPort()))
				thisProcess.ports.add(port);
		}
	}

	public String getProcessName() {
		/**
		 * EFFECTS: Returns this process's name
		 */
		return processName;
	}

	public HashSet<MyPort> getPorts() {
		/**
		 * EFFECTS: Returns the ports this process has open
		 */
		return ports;
	}

	public int getPID() {
		/**
		 * EFFECTS: Returns this process's PID
		 */
		return PID;
	}

	public double getUp() {
		/**
		 * EFFECTS: Returns this process's upload traffic
		 */
		return up;
	}

	public double getDown() {
		/**
		 * EFFECTS: Returns this process's download traffic
		 */
		return down;
	}

	public void setUp(double up) {
		/**
		 * MODIFIES: this.up EFFECTS: Sets this process's upload traffic to up
		 */
		this.up = up;
	}

	public void setDown(double down) {
		/**
		 * MODIFIES: this.down EFFECTS: Sets this process's download traffic to
		 * down
		 */
		this.down = down;
	}

	public void clearSpeed() {
		/**
		 * MODIFIES: this.up, this.down EFFECTS: Sets this process's upload and
		 * download traffic to 0
		 */
		this.up = 0;
		this.down = 0;
	}

	public boolean portExists(int port) {
		/**
		 * EFFECTS: Returns true if the specified port is opened by this process
		 * false if it doesn't
		 */
		Iterator<MyPort> itr = ports.iterator();
		while (itr.hasNext()) {
			int portz = itr.next().getPort();
			if (portz == port) {
				return true;
			}
		}
		return false;
	}

	public static boolean processExists(int PID) {
		/**
		 * EFFECTS: Returns true if the process specified by PID exists false if
		 * it doesn't
		 */
		Iterator<RunningProcess> itr = RunningProcesses.iterator();
		while (itr.hasNext()) {
			if (itr.next().getPID() == PID) {
				return true;
			}
		}
		return false;
	}

	public static RunningProcess findProcessByPID(int PID) {
		/**
		 * EFFECTS: Returns the process with the same PID as specified null if
		 * it doesn't
		 */
		Iterator<RunningProcess> itr = RunningProcesses.iterator();
		while (itr.hasNext()) {
			RunningProcess rp = itr.next();
			if (rp.getPID() == PID) {
				return rp;
			}
		}
		return null;
	}

	public static RunningProcess findProcessByPort(int port) {
		/**
		 * EFFECTS: Returns the process that has the specified port open null if
		 * it doesn't exist
		 */
		Iterator<RunningProcess> itr = RunningProcesses.iterator();
		while (itr.hasNext()) {
			RunningProcess rp = itr.next();
			Iterator<MyPort> portIterator = rp.getPorts().iterator();
			while (portIterator.hasNext()) {
				if (portIterator.next().getPort() == port) {
					return rp;
				}
			}
		}
		return null;
	}

	@Override
	public int compareTo(RunningProcess rp) {
		/**
		 * EFFECTS: Puts an order to compare processes by name
		 */
		return processName.compareToIgnoreCase(rp.getProcessName());

	}
	
	public boolean repOk() {
		/**
		 * EFFECTS: Returns true if the rep invariant holds for this
		 * 			else returns false
		 */
		return ports.size() > 0;
	}
	
	public String toString() {
		/**
		 * EFFECTS: Returns a String representation
		 */
		String s = processName;
		Iterator<MyPort> itr = ports.iterator();
		while(itr.hasNext()) {
			s = s + itr.next().getPort();
		}
		s = s + "Down: " + down + "Up: " + up;
		return s;
	}
}
